package com.ming.ming;

import java.util.List;

import com.ming.ming.model.MingMessage;

import net.lvsq.jgossip.core.GossipService;
import net.lvsq.jgossip.event.GossipListener;
import net.lvsq.jgossip.model.GossipMember;
import net.lvsq.jgossip.model.GossipState;

/*
 * @Author: Bowen 849084209@qq.com
 * @Date: 2023-02-01 21:56:58
 */
public class MingConfig {

    private volatile GossipService gossipService = null;

    private Integer port;

    private String ipAddress;

    private String id;
    
    /**
     * 记录当前leader的 id(port)
     * -1 是没人在位
     */
    private volatile Integer leaderId = -1;
    
    // 自己是否在选举中
    private volatile boolean electioning = false;
    private volatile long electioningTime = 0L;

    public static final String election = "election";
    public static final String alive = "alive";
    public static final String victory = "victory";
    // 分割符
    public static final String fenGe = ";";

    private static final Integer everyOne = 0;

    // 获取 配置文件集群地址

    // 根据集群地址的端口数值大小来判断谁是leader ,并封装 clusterDefinition.class,且保存到内存中

    // 判断谁是leader

    // jgossip 协议封装
    public static MingConfig init(Integer port, String ipAddress, String id) throws Exception {

        MingConfig mingConfig = new MingConfig();
        mingConfig.setPort(port);
        mingConfig.setIpAddress(ipAddress);
        mingConfig.setId(id);
        // 初始化 监听函数
        GossipListener lst = mingConfig.crtListener();

        // 封装 选举服务
        GossipService gs = JGossipUtils.getGossipService(port, ipAddress, id, lst);
        mingConfig.setGossipService(gs);
        // 注册自己
        gs.start();
        return mingConfig;
    }

    // 发送 election : 我可以当老大吗?
    public void sendElection(Integer sourceId, Integer targetId) throws InterruptedException {
    	int count = 0;
    	for(int i = 1 ;i<11;i++ ) {
    		count =  gossipService.getGossipManager().getLiveMembers().size();
    		System.out.println("发"+i+"次消息后查看在线数："+ count);
    		if(count>1) {
    			break;
    		}
    		Thread.sleep(1000);
    	}
    	if(gossipService.getGossipManager().getLiveMembers().size()>1) {
    		String msg = sourceId + fenGe + targetId + fenGe + "election";
    		gossipService.getGossipManager().publish(msg);
    		System.out.println("发送" + msg);
    	}else {
    		System.out.println("单机不用发送选举消息" +gossipService.getGossipManager().getLiveMembers().toString());
    		String msg = sourceId + fenGe + targetId + fenGe + "election";
    		gossipService.getGossipManager().publish(msg);
    		System.out.println("发一次消息后查看在线数："+ gossipService.getGossipManager().getLiveMembers().size());
    	}
    }

    // 发送 alive : 我还活着呢,我比你大!
    public void sendAlive(Integer sourceId, Integer targetId) {
    	String msg = sourceId + fenGe + targetId + fenGe + alive;
        gossipService.getGossipManager().publish(msg);
        System.out.println("发送 ： "+msg);
    }

    // 发送 victory : 我是老大!
    public void sendVictory(Integer sourceId, Integer targetId) {
        gossipService.getGossipManager().publish(sourceId + fenGe + targetId + fenGe + victory);
    }

    // 定时任务发送

    // 倒计时准备造反

    // 主流程
    public GossipListener crtListener() {
        GossipListener listener = (member, state, payload) -> {
        	System.out.println("进来了！");
            if (state == GossipState.RCV) {
                // 接收到消息
                MingMessage mMsg = MingMessage.init(payload);

                System.out.println("********** 有人RCV:" + member + ":" + mMsg.toString() + " *************");
                
                // 如果消息是 选举 
                if (election.equals(mMsg.getMsg())) {
                    // 如果leader还在位并且存活
                    if (leaderId!=-1 && isAlive(leaderId)) {
                        // 发送自己存活
                        sendAlive(port, member.getPort());
                        // 如果当前自己是leader
                        if(port == leaderId){
                            // 广播leaderId
                            sendVictory(port, everyOne);
                            return;
                        }
                    }else{
                        // 比自己小,发送给对方消息. 自己存活.
                        if (port < member.getPort()) {
                            sendAlive(port, member.getPort());
                            // 然后查看自己是否有当老大的资历并且发起选举
                            List<GossipMember> gms = gossipService.getGossipManager().getLiveMembers();
                            boolean leader = true;
                            for (GossipMember gm : gms) {
                                if (getPort() < gm.getPort()) {
                                    leader = false;
                                    break;
                                }
                            }
                            // 如果自己最大. 发起选举
                            if (leader) {
                                callAnElection();
                            }
                        }else {
                        	// 自己退出选举
                        	setElectioning(false);
                        }
                    }


                }
                // 如果消息是存活 并且 自己在选举中 并且 消息是发送给自己的。 取消自己的选举。
                if (alive.equals(mMsg.getMsg()) && isElectioning() && port.equals(mMsg.getTargetId())) {
                    System.out.println("leader 尚在人间!!!!!!!");
                    setElectioning(false);
                    return;
                }
                // 如果是广播 leaderId,接收并直接更新leaderId
                if (victory.equals(mMsg.getMsg())) {
                	// 自己退出选举
                	setElectioning(false);
                    // 设置leaderId
                    setLeaderId(member.getPort());
                    return;
                }
            }
            if (state == GossipState.DOWN) {
                System.out.println("********** 有人DOWN:" + member + ",当前leader: "+leaderId+",判断结果:"+(leaderId.equals(member.getPort()))+" *************");
                // 如果这个人是leader ,那么就重新发起选举
                if (leaderId.equals(member.getPort()) ) {
                    callAnElection();
                    return;
                }

            }
            if (state == GossipState.UP) {
                System.out.println("********** 有人UP:" + member + " *************");
                if(port.equals(leaderId)) {
                	 sendVictory(port, everyOne);
                }
            }
            if (state == GossipState.JOIN) {
                // 如果是自己刚刚启动. 则不需要进行任何操作
                if(port == member.getPort()){
                    System.out.println("自己发现自己###################################");
                    // 设置leader
                    callAnElection();
                    return;
                }else{
                    System.out.println("********** 有人JOIN:" + member + " *************");
                    System.out.println("发现人:"+port+",当前加入人:"+member.getPort());
                }
            }
        };
        return listener;
    }

    /**
     * 发起选举
     */
    private void callAnElection(){
        new Thread(()-> {
            while (null == gossipService ) {
                System.out.println("选举函数 *初始化* ing");
            }
            while(gossipService.getGossipManager().isWorking() == false){
                System.out.println("选举函数 &启动& ing");
            }
            System.out.println("选举函数 $启动完成$ !!!!");
            while(isAlive(port)==false){
                System.out.println("集群注册中ing");
            }
            System.out.println("节点自检完成,发起选举!!!!!!!!!!!!!!");
            // 设置自己为选举中
            setElectioning(true);
            // 发起选举
            System.out.println("!!!!选举中!!!!");
            try {
				sendElection(port, everyOne);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
            waitElection();
        }).start();
    }

    private void waitElection() {
        System.out.println("!!!!等待选举结果!!!!");
		while (isElectioning()) {
            // 经过10S 没有人投反对票
            if ((System.currentTimeMillis() - this.electioningTime) > 10000) {
                // 竞选中
                if (isElectioning()) {
                    // 结束竞选
                    setElectioning(false);
                    System.out.println("!!!!"+port+"当选leader!!!!");
                    // 设置leader
                    setLeaderId(port);
                    // 广播leaderId
                    sendVictory(port, everyOne);
                }else{
                     // 结束竞选
                     setElectioning(false);
                     System.out.println("!!!!选举结束!!!!");
                }
            }
        }
    }
    
    
    
    /**
     * 检查指定Id是否存活
     * 
     * @return
     */
    public boolean isAlive(Integer port) {
        try {
            boolean isAlive = gossipService.getGossipManager().getLiveMembers().stream().filter(m -> m.getPort().equals(port))
            .findAny().isPresent();
            return isAlive;
        } catch (Exception e) {
            System.out.println("调用方法是否存活报错 : " + e.getCause());
            return false;
        }
    }
    
    // -------------------------------------------------------------------------------

    public GossipService getGossipService() {
        return gossipService;
    }

    public void setGossipService(GossipService gossipService) {
        this.gossipService = gossipService;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public String getIpAddress() {
        return ipAddress;
    }

    public void setIpAddress(String ipAddress) {
        this.ipAddress = ipAddress;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public static String getElection() {
        return election;
    }

    public static String getAlive() {
        return alive;
    }

    public static String getVictory() {
        return victory;
    }

    public static String getFenge() {
        return fenGe;
    }

    public boolean isElectioning() {
        return electioning;
    }

    synchronized public void setElectioning(boolean electioning) {
        this.electioning = electioning;
        if(electioning){
            this.electioningTime = System.currentTimeMillis();
        }
    }

    public Integer getLeaderId() {
        return leaderId;
    }

    public void setLeaderId(Integer leaderId) {
        this.leaderId = leaderId;
    }

    public long getElectioningTime() {
        return electioningTime;
    }

    public void setElectioningTime(long electioningTime) {
        this.electioningTime = electioningTime;
    }

}
